<template>
  <div :class="['wrap', className]" :style="$attrs">
    <Masking v-model="showMask">
      <div class="swipe" :style="$attrs">
        <div class="container" :style="{transform: `translateX(${-translateX}px)`, transition: `${dealy}ms`}">
          <div v-for="(l,i) in list" :key="i" class="item-container" :style="setItemTransition(i)">
            <div :id="`swipe-item-${i}`" :class="['item', {'cancel-scale': !zoom}]" @touchmove="e=>touchmoveHandle(e,i)" @touchstart="e=>touchstartHandle(e,i)" @touchend="e=>touchendHandle(e,i)">
              <slot v-bind="{scoped: l}">
                <ImageVideo :host="host" :src="l.src" :poster="l.poster" :fit="fit" :is-image="l.type === 'image'" :watermark="watermark" :landscape="landscape" @video-click="videoClickHandle" @requestFullscreen="requestFullscreenHandle" @exitFullscreen="exitFullscreenHandle" />
              </slot>
            </div>
          </div>
        </div>
        <div v-show="list.length>1&&list[current].type!=='video'" class="indicator">
          <slot name="indicator">
            <div class="custom-indicator"><span class="curent-index">{{ index }}</span>/<span>{{ length }}</span></div>
          </slot>
        </div>
        <div v-if="zoom" class="close-scale" @click="closeClickHandle">
          <svg-icon icon-class="cuowuguanbiquxiao" />
        </div>
      </div>
    </Masking>
  </div>
</template>
<script>
import ImageVideo from '@/components/ImageVideo'
import Masking from '@/components/Masking'
function getDistance(obj) {
  return Math.sqrt(
    (obj.x1 - obj.x0) ** 2 +
      (obj.y1 - obj.y0) ** 2
  )
}
export default {
  name: 'Swipe',
  components: {
    ImageVideo,
    Masking
  },
  props: {
    list: {
      type: Array,
      default: () => []
      // {
      //   type: 'image' or 'video'
      //   src: '/sss/sss/sss.jpg'
      // }
    },
    value: {
      type: Number,
      default: 0
    },
    dealy: {
      type: Number,
      default: 500
    },
    landscape: {
      type: Boolean,
      default: false // landscape portrait
    },
    className: {
      type: String,
      default: ''
    },
    fit: {
      type: String,
      default: 'contain'
    },
    watermark: {
      type: Boolean,
      default: false
    },
    zooming: {
      type: Boolean,
      default: false
    },
    host: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      showMask: false,
      current: 0,
      left: 0,
      itemWidth: 0,
      startObj: null,
      // touch Move
      moveObj: null,
      zoom: false,
      initScale: 1,
      scale: 1,
      startScale: 1,
      startDistance: 1,
      startLeft: 0,
      startTop: 0,
      isFullScreen: false
    }
  },
  computed: {
    translateX() {
      if (this.moveObj !== null && !this.zoom) {
        return this.left + this.moveObj.x0
      }
      return this.left
    },
    xKey() {
      return this.landscape ? 'clientY' : 'clientX'
    },
    yKey() {
      return this.landscape ? 'clientX' : 'clientY'
    },
    hasVideo() {
      return this.list.some(e => e.type === 'video')
    },
    length() {
      return this.hasVideo ? this.list.length - 1 : this.list.length
    },
    index() {
      return this.hasVideo ? this.current : this.current + 1
    }
  },
  watch: {
    value: {
      handler(n) {
        if (this.$isType(n, 'Number')) {
          this.current = n
          setTimeout(() => {
            this.left = this.getLeft(this.current)
          }, 0)
        }
      },
      immediate: true
    },
    current: {
      handler(n) {
        this.$emit('input', n)
      },
      immediate: true
    }
  },
  mounted() {
    this.getSwipeWidth()
  },
  methods: {
    getXY(e) {
      const touches = e.touches.length > 0 ? e.touches : e.changedTouches
      const obj = Object.create(null)
      obj.touches = touches
      obj.length = touches.length
      obj.multiple = false
      obj.x0 = touches[0][this.xKey]
      obj.y0 = touches[0][this.yKey]
      obj.target = e.target
      if (touches.length >= 2) {
        obj.x1 = touches[1][this.xKey]
        obj.y1 = touches[1][this.yKey]
        obj.multiple = true
      }
      return obj
    },
    touchstartHandle(e, i) {
      this.startObj = this.getXY(e)
      if (e.touches.length >= 2) {
        this.startScale = this.scale || this.initScale
        this.startDistance = getDistance(this.startObj)
      }
      // moveing
      if (e.touches.length === 1) {
        const el = this.$el.querySelector(`#swipe-item-${i}`)
        const { left, top } = el.getBoundingClientRect()
        this.startLeft = left
        this.startTop = top
      }
    },
    touchendHandle(e, i) {
      if (this.isFullScreen) return
      const obj = this.getXY(e)
      if (this.startObj !== null && !this.zoom && !obj.multiple) {
        const sub = obj.x0 - this.startObj.x0
        const tagName = obj.target.tagName
        const subY = obj.y0 - this.startObj.y0 // 处理滑动过程中出现上下滑时禁止左右滑动

        if ((Math.abs(sub) > 80 && Math.abs(subY) < 80) || tagName === 'XG-PLAYBACKRATE' || tagName === 'VIDEO') {
          if (sub > 0) {
            if (i <= 0) {
              this.current = this.list.length - 1
            } else {
              this.current = i - 1
            }
            this.left = this.getLeft(this.current)
          } else if (sub < 0) {
            if (i >= this.list.length - 1) {
              this.current = 0
            } else {
              this.current = i + 1
            }
            this.left = this.getLeft(this.current)
          }
        }
      }
      this.startObj = null
      this.moveObj = null
    },
    getLeft(l) {
      this.closeClickHandle()
      return this.itemWidth * l
    },
    getSwipeWidth() {
      let ele = this.$el.querySelector('.swipe').parentElement.parentElement
      if (!ele) {
        ele = this.$el.querySelector('.swipe')
      }
      const width = window.getComputedStyle(ele).width || ele.getBoundingClientRect().width
      if (width && this.$isType(width, 'string')) {
        this.itemWidth = Math.floor(Number(width.replace(/[^\d.]+/, '')))
      } else {
        this.itemWidth = Number(width)
      }
    },
    touchmoveHandle(e, i) {
      if (this.list.length === 1) return
      if (this.startObj === null) return
      if (this.isFullScreen) return
      this.$emit('touch')
      const obj = this.getXY(e)
      const subY = obj.y0 - this.startObj.y0 // 处理上下滑情况
      if (Math.abs(subY) < 80) {
        // 处理滑动过程中出现上下滑时禁止左右滑动
        return
      }

      // video
      const tagName = obj.target.tagName
      if (tagName === 'XG-PLAYBACKRATE' || tagName === 'VIDEO') {
        this.$nextTick(() => {
          this.stopPlay(this.$children)
        })
        this.touchendHandle(e, i)
        return
      }
      if (this.stopEvent(e)) return
      this.moveObj = obj
      if (obj.target.tagName !== 'IMG') return
      const el = this.$el.querySelector(`#swipe-item-${i}`)
      if (this.startObj !== null && this.moveObj !== null && this.moveObj.multiple && this.zooming) {
        const distance = getDistance(this.moveObj)
        let scale = this.scale * distance / this.startDistance
        this.scale = scale
        // this.$dialog.alert({ message: `scale=${scale},startScale=${this.startScale},distance=${distance},this.startDistance=${this.startDistance}` })
        scale = Math.min(Math.max(scale, 1 / 3), 3)
        el.style.transform = `scale(${scale}, ${scale})`
        this.zoom = true
      } else if (this.zoom && this.zooming && this.startObj !== null) {
        const { startObj, moveObj } = this
        const { width, height } = el.parentElement.getBoundingClientRect()
        const nTop = startObj.y0 - moveObj.y0 - this.startLeft
        const nLeft = moveObj.x0 - startObj.x0 + this.startTop
        // console.log('nTop=', nTop, ',nLeft=', nLeft, this.startLeft, this.startTop)
        if (nTop < width - 20) {
          el.style.top = nTop + 'px'
        }
        if (nLeft < height - 20) {
          el.style.left = nLeft + 'px'
        }
      }
    },
    stopEvent(e) {
      let parent = e.target && e.target.parentElement
      let flag = false
      while (parent && !flag) {
        flag = parent.classList.contains('xgplayer-controls')
        parent = parent.parentElement
      }
      return flag
    },
    // 暂停播放
    stopPlay(children) {
      for (const p of children) {
        if (p.$options.name === 'Player' && p.player && p.player.hasStart) {
          p.player.pause && p.player.pause()
        }
        if (p.$children) {
          this.stopPlay(p.$children)
        }
      }
    },
    setItemTransition(i) {
      const maxIndex = this.list.length - 1
      const style = { width: `${this.itemWidth}px` }
      if (this.moveObj !== null && !this.zoom) {
        if (this.current === 0) {
          if (i === maxIndex) {
            style.transform = `translateX(${-this.getLeft(maxIndex + 1)}px)`
          }
        } else if (this.current === maxIndex) {
          if (i === 0) {
            style.transform = `translateX(${this.getLeft(maxIndex + 1)}px)`
          }
        }
      }
      return style
    },
    closeClickHandle() {
      const els = this.$el.querySelectorAll(`.item`)
      for (const e of els || []) {
        e.style.top = ''
        e.style.left = ''
        e.transform = 'inherit'
      }
      this.zoom = false
      this.scale = 1
      this.startScale = this.initScale
      this.startDistance = 1
      this.startLeft = 0
      this.startTop = 0
    },
    videoClickHandle() {
      this.$emit('video-click')
    },
    requestFullscreenHandle() {
      this.isFullScreen = true
    },
    exitFullscreenHandle() {
      this.isFullScreen = false
    }
  }
}
</script>
<style lang="less" scoped>
.wrap{
  width: 375px;
  height: 375px;
  .swipe{
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: relative;
    .container{
      height: 100%;
      display: flex;
      align-items: stretch;
      .item-container{
        flex: 0 0 auto;
        height: 100%;
        font-size: 0;
        .item{
          // transition: all 50ms;
          height: 100%;
          position: relative;
          z-index: 1;
        }
      }
    }
    .indicator{
      position: absolute;
      right: 20px;
      bottom: 5px;
      padding: 2px 5px;
      font-size: 12px;
      color: #fff;
      background: rgba(0, 0, 0, 0.1);
      border-radius: 4px;
    }
    .close-scale{
      position: absolute;
      right: 20px;
      top: 5px;
      padding: 2px 5px;
      font-size: 12px;
      background: rgba(0, 0, 0, 0.1);
      color: #fff;
      width: 40px;
      height: 40px;
      border-radius: 50%;
      box-sizing: border-box;
      display: flex;
      align-items: center;
      justify-content: center;
      &>svg{
        width: 24px;
        height: 24px;
      }
    }
  }
  .curent-index{
    font-size: 18px;
  }
  .cancel-scale{
    transform: scale(1) !important;
  }
}
</style>
